package com.lzp.systemopt.camera.impl

import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.*
import android.hardware.camera2.*
import android.hardware.camera2.CameraManager
import android.media.ImageReader
import android.os.Handler
import android.os.HandlerThread
import android.util.Log
import android.util.Size
import android.view.Surface
import android.view.TextureView
import androidx.core.content.ContextCompat
import com.lzp.systemopt.camera.*
import com.lzp.systemopt.camera.save.ImageSaver
import com.lzp.systemopt.camera.view.CameraSurfaceView
import com.utils.LogUtils
import com.utils.PathUtils
import java.io.File
import java.util.*

/**
 * @description:
 * @author: lxp
 * @date: 2022/1/27 下午2:49
 */
class CapturePictureImpl : ICamera, ICapturePicture {

    private val TAG = "CapturePictureImpl"

    private lateinit var mActivity: Activity
    private lateinit var mTextureView: CameraSurfaceView
    private lateinit var mCameraManager: CameraManager

    private var mBackgroundThread: HandlerThread? = null
    private var mBackgroundHandler: Handler? = null

    // 相机属性
    private var mCameraCharacteristics: CameraCharacteristics? = null
    private var mImageReader: ImageReader? = null

    // 预览大小
    private var mPreviewSize: Size = Size(0, 0)

    // 相机预览请求
    private var mPreviewRequest: CaptureRequest? = null
    private var mCameraCaptureSession: CameraCaptureSession? = null

    private var mCaptureListener: CaptureListener? = null

    // 相机对象
    private var mCameraDevice: CameraDevice? = null


    private val mSurfaceTextureListener = object : TextureView.SurfaceTextureListener {
        override fun onSurfaceTextureAvailable(texture: SurfaceTexture, width: Int, height: Int) {
            openCamera(width, height)
        }

        override fun onSurfaceTextureSizeChanged(texture: SurfaceTexture, width: Int, height: Int) {
            configureTransform(width, height)
        }

        override fun onSurfaceTextureDestroyed(texture: SurfaceTexture) = true

        override fun onSurfaceTextureUpdated(texture: SurfaceTexture) = Unit

    }

    private fun configureTransform(viewWidth: Int, viewHeight: Int) {
        val rotation = mActivity.windowManager.defaultDisplay.rotation
        val matrix = Matrix()
        val viewRect = RectF(0f, 0f, viewWidth.toFloat(), viewHeight.toFloat())
        val bufferRect = RectF(0f, 0f, mPreviewSize.height.toFloat(), mPreviewSize.width.toFloat())
        val centerX = viewRect.centerX()
        val centerY = viewRect.centerY()

        if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
            bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY())
            val scale = Math.max(
                viewHeight.toFloat() / mPreviewSize.height,
                viewWidth.toFloat() / mPreviewSize.width
            )
            with(matrix) {
                setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL)
                postScale(scale, scale, centerX, centerY)
                postRotate((90 * (rotation - 2)).toFloat(), centerX, centerY)
            }
        } else if (Surface.ROTATION_180 == rotation) {
            matrix.postRotate(180f, centerX, centerY)
        }
        mTextureView.setTransform(matrix)
    }

    private val onImageAvailableListener = ImageReader.OnImageAvailableListener {
        val file = File(com.lzp.systemopt.camera.CameraManager.getInstance().generatePicturePath())
        Log.d(TAG, ": ${file.absolutePath}  $mCaptureListener")
        mBackgroundHandler?.post(ImageSaver(it.acquireNextImage(), file, mCaptureListener))
    }

    override fun startCapturePicPreView() {
        startBackgroundThread()
        if (mTextureView.isAvailable) {
            openCamera(mTextureView.width, mTextureView.height)
        } else {
            mTextureView.surfaceTextureListener = mSurfaceTextureListener
        }
    }


    private fun openCamera(width: Int, height: Int) {
        mPreviewSize = Size(width, height)
        val permission = ContextCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA)
        if (permission != PackageManager.PERMISSION_GRANTED) {
            LogUtils.d(TAG, "has not camera permission")
            return
        }
        try {
            mCameraCharacteristics =
                mCameraManager.getCameraCharacteristics(getCameraId().toString())
            mImageReader = ImageReader.newInstance(
                mPreviewSize.width,
                mPreviewSize.height,
                ImageFormat.JPEG,
                2
            ).apply {
                setOnImageAvailableListener(onImageAvailableListener, mBackgroundHandler)
            }
            mCameraManager.openCamera(getCameraId().toString(), stateCallback, mBackgroundHandler)

        } catch (e: Exception) {
            LogUtils.e(TAG, "open camera exception")
        }
    }

    private val stateCallback = object : CameraDevice.StateCallback() {
        override fun onOpened(cameraDevice: CameraDevice) {
            this@CapturePictureImpl.mCameraDevice = cameraDevice
            createCameraPreviewSession()
        }

        override fun onDisconnected(cameraDevice: CameraDevice) {
            cameraDevice.close()
            this@CapturePictureImpl.mCameraDevice = null
        }

        override fun onError(cameraDevice: CameraDevice, error: Int) {
            onDisconnected(cameraDevice)
        }
    }

    private fun createCameraPreviewSession() {
        if (mCameraDevice == null) return
        if (mImageReader == null) return
        val imageReader = mImageReader!!
        val cameraDevice = mCameraDevice!!
        val previewSurface = Surface(mTextureView.surfaceTexture)

        configureTransform(mPreviewSize.width, mPreviewSize.height)
        try {
            val previewRequestBuilder =
                cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
            previewRequestBuilder.addTarget(previewSurface)
            cameraDevice.createCaptureSession(
                listOf<Surface>(previewSurface, imageReader.surface),
                object : CameraCaptureSession.StateCallback() {
                    override fun onConfigured(session: CameraCaptureSession) {
                        mCameraCaptureSession = session
                        mPreviewRequest = previewRequestBuilder.build()
                        startPreview()
                    }

                    override fun onConfigureFailed(session: CameraCaptureSession) {
                        LogUtils.d(TAG, "ConfigureFailed. session: mCaptureSession")
                    }
                },
                mBackgroundHandler
            )
        } catch (e: Exception) {
            LogUtils.d(TAG, "camera create preview session fail.")
        }
    }

    private fun startPreview() {
        if (mCameraCaptureSession == null) return
        if (mPreviewRequest == null) return
        try {
            mCameraCaptureSession?.setRepeatingRequest(mPreviewRequest!!, null, mBackgroundHandler)
        } catch (e: Exception) {
            LogUtils.d(TAG, "start preview fail")
        }
    }

    override fun stopCapturePicPreView() {
        releaseCamera("stop")
    }

    override fun capturePic() {
        try {
            if (mImageReader == null) return
            val createCaptureRequest =
                mCameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)?.apply {
                    addTarget(mImageReader!!.surface)
                   /* //自动对焦
                    set(
                        CaptureRequest.CONTROL_AF_MODE,
                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
                    )
                    //自动曝光
                    set(
                        CaptureRequest.CONTROL_AE_MODE,
                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH
                    )
                    //调整方向
                    val rotation = mActivity.windowManager.defaultDisplay.rotation
                    set(
                        CaptureRequest.JPEG_ORIENTATION,
                        com.lzp.systemopt.camera.CameraManager.getInstance().getOrientation(rotation)
                    )*/
                }?.build()
            createCaptureRequest?.let {
                Log.d(TAG, "capturePicture: ")
                val callback = object : CameraCaptureSession.CaptureCallback() {
                    override fun onCaptureCompleted(
                        session: CameraCaptureSession,
                        request: CaptureRequest,
                        result: TotalCaptureResult
                    ) {
                        super.onCaptureCompleted(session, request, result)
                        LogUtils.d(TAG, "onCaptureCompleted: ${result.toString()}")
                        startPreview()
                    }
                }
                mCameraCaptureSession?.apply {
                    stopRepeating()
                    abortCaptures()
                    capture(createCaptureRequest, callback, mBackgroundHandler)
                }
            }
        } catch (e: CameraAccessException) {
            Log.e(TAG, e.toString())
        }
    }

    override fun initCamera(activity: Activity, cameraSurfaceView: CameraSurfaceView) {
        this.mActivity = activity
        this.mTextureView = cameraSurfaceView
        this.mCameraManager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
    }

    override fun setCaptureListener(listener: CaptureListener?) {
        this.mCaptureListener = listener
    }

    override fun requestSwitchCamera() {
        releaseCamera("switchCamera")
        openCamera(mPreviewSize.width, mPreviewSize.height)
    }

    private fun getCameraId(): Int {
        return com.lzp.systemopt.camera.CameraManager.mCameraId
    }

    override fun releaseCamera(reason: String) {
        try {
            mCameraCaptureSession?.close()
            mCameraCaptureSession = null
            mCameraDevice?.close()
            mCameraDevice = null
            mImageReader?.close()
            mImageReader = null
            mBackgroundThread?.quitSafely()
            mBackgroundThread?.join()
            mBackgroundThread = null
            mBackgroundHandler = null
        } catch (e: InterruptedException) {
            throw RuntimeException("Interrupted while trying to lock camera closing.", e)
        }
    }

    private fun startBackgroundThread() {
        val handlerThread = HandlerThread("cameraBackground")
        handlerThread.start()
        mBackgroundThread = handlerThread
        mBackgroundHandler = Handler(handlerThread.looper)
    }
}